﻿using Orleans;
using Orleans.Runtime;
using Orleans.Storage;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace iTool.ClusterComponent
{
    public class iToolStorageHandel : IGrainStorage, ILifecycleParticipant<ISiloLifecycle>
    {
        private readonly string ProviderName;
        private readonly IServiceProvider serviceProvider;
        private readonly IKeyValueStorageProvider storageProvider;

        public iToolStorageHandel(string providerName, IServiceProvider serviceProvider, IKeyValueStorageProvider storageProvider)
        {
            this.ProviderName = providerName;
            this.serviceProvider = serviceProvider;
            this.storageProvider = storageProvider;
        }

        #region IGrainStorage (执行优先级 2)
        public async Task ClearStateAsync(string grainType, GrainReference grainReference, IGrainState grainState)
        {
            var key = this.GetKey(grainType, grainReference);
            await this.storageProvider.RemoveAsync(key);
        }

        public async Task ReadStateAsync(string grainType, GrainReference grainReference, IGrainState grainState)
        {
            var key = this.GetKey(grainType, grainReference);
            var type = grainState.State.GetType();
            grainState.State = await this.storageProvider.GetOrAddAsync(key, () => Activator.CreateInstance(type), type) ?? Activator.CreateInstance(type);
        }

        public async Task WriteStateAsync(string grainType, GrainReference grainReference, IGrainState grainState)
        {
            var key = this.GetKey(grainType, grainReference);
            await this.storageProvider.AddOrUpdateAsync(key, grainState.State, grainState.State.GetType());
        }
        #endregion

        #region ILifecycleParticipant<ISiloLifecycle> (执行优先级 1)
        public void Participate(ISiloLifecycle lifecycle)
        {
            lifecycle.Subscribe(OptionFormattingUtilities.Name<iToolStorageHandel>(this.ProviderName), ServiceLifecycleStage.ApplicationServices, Init, Close);
        }


        public Task Init(CancellationToken ct)
        {
            return Task.CompletedTask;
        }

        public Task Close(CancellationToken ct)
        {
            return Task.CompletedTask;
        }

        #endregion

        private string GetKey(string className, GrainReference grainReference)
        {
            var keyType = iUtils.GetKeyTypeOfClass(className);
            string key = string.Empty;

            switch (keyType)
            {
                case "long":
                    key = grainReference.GetPrimaryKeyLong().ToString();
                    break;
                case "string":
                    key = grainReference.GetPrimaryKeyString();
                    break;
                case "long-string":
                    long longKey = grainReference.GetPrimaryKeyLong(out key);
                    key = $"{longKey}_{key}";
                    break;

                case "guid-string":
                case "guid":
                default:
                    key = grainReference.ToKeyString();
                    break;
            }

            return $"{className}_{key}";
        }

    }
}
